#ifndef LOD_OBJECT3D_H
#define LOD_OBJECT3D_H

#include <vector>

#include "base/material.h"
#include "base/triangle.h"

//! 3D object
template <class vtype, class cvtype, 
         class co_type, class comp_type, class it_type>
class object3d
{
    public:
   //! vertices
   std::vector<vtype> verts;
   //! coloured vertices
   std::vector<cvtype> cverts;
        
   //! non-coloured triangles (they will be shaded exactly as the material says)
   std::vector<triangle<it_type,it_type> > tris;
   //! coloured triangles (during shading some components of the material might be overwritten by the specified colour)
   std::vector<triangle<it_type,it_type> > ctris;

   //! materials
   std::vector<material<comp_type> > mats;

   //! vertices count
   int vcount;
   //! coloured vertices count
   int cvcount;

   //! materials count
   int mcount;

   //! non-coloured triangles count
   int tcount;
   //! coloured triangles count
   int ctcount;

   //! vertex'es x coordinate's smallest (index 0) and biggest value (1)
   /*! x[coordinate]bord[ers] */
   co_type xbord[2];
   //! vertex'es y coordinate's smallest (index 0) and biggest value (1)
   /*! y[coordinate]bord[ers] */
   co_type ybord[2];
   //! vertex'es z coordinate's smallest (index 0) and biggest value (1)
   /*! y[coordinate]bord[ers] */
   co_type zbord[2];

   //! the value of the negative infinity
   /*! when compared, values lower than neg_inf ~ equals minus infinity */
   co_type neg_inf;
   //! the value of the positive infinity
   /*! when compared, values higher than pos_inf ~ equals plus infinity */
   co_type pos_inf;

   /*!
    * \param ninf Value of negative infinity
    * \param pinf Value of positive infinity
    * \param vreserve Amount of vertices to reserve space for
    * \param cvreserve Amount of coloured vertices to reserve space for
    * \param treserve Number of triangles to reserve space for
    * \param mreserve Number of materials to reserve space for*/
   object3d(co_type ninf, co_type pinf, 
             int vreserve=0, int treserve=0, int mreserve=0, int cvreserve=0, int ctreserve=0) 
    :  vcount(0), cvcount(0), mcount(0), tcount(0), ctcount(0), 
       neg_inf(ninf), pos_inf(pinf)
   {
      if(vreserve>0)
         verts.reserve(vreserve);
      if(treserve>0)
         tris.reserve(treserve);
      if(mreserve>0)
         mats.reserve(mreserve);
      if(cvreserve>0)
         verts.reserve(cvreserve);
      if(ctreserve>0)
         ctris.reserve(ctreserve);

      xbord[0] = pos_inf;
      ybord[0] = pos_inf;
      zbord[0] = pos_inf;

      xbord[1] = neg_inf;
      ybord[1] = neg_inf;
      zbord[1] = neg_inf;
   }

   //! clears all of the data of the 3D object 
   void clear() 
   {
      verts.clear();
      mats.clear();
      tris.clear();
      cverts.clear();
      ctris.clear();

      vcount=0;
      mcount=0;
      tcount=0;
      cvcount=0;
      ctcount=0;

      xbord[0] = pos_inf;
      ybord[0] = pos_inf;
      zbord[0] = pos_inf;
      xbord[1] = neg_inf;
      ybord[1] = neg_inf;
      zbord[1] = neg_inf;
   }

   //! Comparison operator
   bool operator== (const object3d<vtype,cvtype,co_type,comp_type,it_type> & ob) const
   {
      return (verts==ob.verts &&
              mats==ob.mats && 
              tris==ob.tris &&
              cverts==ob.cverts &&
              ctris==ob.ctris &&
              vcount==ob.vcount &&
              tcount==ob.tcount &&
              mcount==ob.mcount &&
              cvcount==ob.cvcount &&
              ctcount==ob.ctcount
              // since borders are implied by vertices, do not compare them here
             );
   }

   //! // Center mesh around origin.
   /*! Fit mesh in a box from (-1, -1, -1) to (1, 1, 1) */
   void normalize()
   {
       vector3<co_type> minv(xbord[0],ybord[0],zbord[0]);
       vector3<co_type> maxv(xbord[1],ybord[1],zbord[1]);
       vector3<co_type> dimv(maxv.x-minv.x, maxv.y-minv.y, maxv.z-minv.z);
       co_type max;
       if(dimv.x>=dimv.y && dimv.x>=dimv.z) max=dimv.x;
       else if(dimv.y>=dimv.z) max=dimv.y;
       else max=dimv.z;

       float scale=2.0/max;
       vector3<co_type> transv(0.5*(minv.x+maxv.x), 0.5*(minv.y+maxv.y), 0.5*(minv.z+maxv.z));

       for(int i=0; i<vcount; i++) {
           verts[i].x = scale*(verts[i].x-transv.x);
           verts[i].y = scale*(verts[i].y-transv.y);
           verts[i].z = scale*(verts[i].z-transv.z);
       }
       for(int i=0; i<cvcount; i++) {
           cverts[i].x = scale*(cverts[i].x-transv.x);
           cverts[i].y = scale*(cverts[i].y-transv.y);
           cverts[i].z = scale*(cverts[i].z-transv.z);
       }
       xbord[0] = scale*(xbord[0]-transv.x);
       ybord[0] = scale*(ybord[0]-transv.y);
       zbord[0] = scale*(zbord[0]-transv.z);

       xbord[1] = scale*(xbord[1]-transv.x);
       ybord[1] = scale*(ybord[1]-transv.y);
       zbord[1] = scale*(zbord[1]-transv.z);
   }
};


#endif // LOD_OBJECT3D_H
